package bosc.cn.server;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author tianshq
 * @date 2025/9/6 22:13
 * @description 内网客户端WebSocket端
 */
@Component
@ServerEndpoint(value = "/tunnel/client/{clientId}",
        configurator = ServerEndpointConfig.Configurator.class)
@Slf4j
public class InternalClientEndpoint {

    // 存储内网客户端会话
    private static final Map<String, Session> clientSessions = new ConcurrentHashMap<>();
    // 存储请求ID与外网用户会话的映射关系
    private static final Map<String, Session> requestSessions = new ConcurrentHashMap<>();
    public static final String WEBSOCKET_REGEX = "|";

    @OnOpen
    public void onOpen(Session session, @PathParam("clientId") String clientId) {
        clientSessions.put(clientId, session);
        log.info("内网客户端连接成功: clientId={}, sessionId={}", clientId, session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            log.info("收到内网客户端消息: sessionId={}, message={}", session.getId(), message);

            requestSessions.forEach((requestId, value) -> log.info("当前所有request session: requestId={}, sessionId={}", requestId, value.getId()));

            // 消息格式: requestId|response_data
            String[] parts = message.split("\\|", 2);
            if (parts.length >= 2) {
                String requestId = parts[0];
                String responseData = parts[1];

                // 找到对应的外网用户会话并发送响应
                Session userSession = requestSessions.get(requestId);
                if (userSession != null && userSession.isOpen()) {
                    log.info("找到外网用户会话: requestId={}, responseData:{}", requestId, responseData);
                    userSession.getBasicRemote().sendText(responseData);
                    requestSessions.remove(requestId); // 清理请求记录
                }
            }
        } catch (Exception e) {
            log.error("处理内网客户端消息异常:{}", e.getMessage());
        }
    }

    @OnClose
    public void onClose(Session session, @PathParam("clientId") String clientId) {
        clientSessions.remove(clientId);
        log.info("内网客户端断开连接: clientId={}", clientId);
    }

    @OnError
    public void onError(Session session, Throwable error, @PathParam("clientId") String clientId) {
        log.error("内网客户端连接异常: clientId={}", clientId, error);
        clientSessions.remove(clientId);
    }

    // 提供给外部调用的方法：转发请求到内网客户端
    public static boolean forwardRequest(String clientId, String requestId, String requestData) {
        // 遍历clientSessions，找到以clientId开头的key,三个板子约定固定的开头id，避免内网ip发生变化

        for (Map.Entry<String, Session> entry : clientSessions.entrySet()) {
            String key = entry.getKey();
            log.info("遍历内网客户端: key={}", key);
            if (key.startsWith(clientId)) {
                clientId = key;
                log.info("找到内网客户端: clientId={}", clientId);
                break;
            }
        }


        Session clientSession = clientSessions.get(clientId);
        if (clientSession != null && clientSession.isOpen()) {
            try {
                log.info("转发请求到内网客户端: clientId={}, requestId={}", clientId, requestId);
                // 消息格式: requestId|response_data
                String message = requestId + WEBSOCKET_REGEX + requestData;
                clientSession.getBasicRemote().sendText(message);
                return true;
            } catch (Exception e) {
                log.error("转发请求到内网客户端失败: clientId={}", clientId, e);
                return false;
            }
        }
        return false;
    }

    // 注册请求会话
    public static void registerRequestSession(String requestId, Session userSession) {
        requestSessions.put(requestId, userSession);
    }

    public static Set<String> getConnectedClients() {
        return clientSessions.keySet();
    }
}

